// 数据库连接配置文件
// 该文件负责配置和初始化PostgreSQL数据库连接

// 引入Sequelize ORM框架
// Sequelize: ORM框架，用于数据库操作
// ORM（对象关系映射）允许使用面向对象的方式操作数据库
const { Sequelize } = require('sequelize');

// 引入应用配置文件
// config: 应用配置对象，包含数据库连接信息
const config = require('../config');

// 从配置文件获取数据库配置信息
// database: 数据库配置对象
const { database } = config;

/**
 * 创建Sequelize实例，配置数据库连接
 * @type {Sequelize}
 * 
 * Sequelize构造函数参数说明:
 * 1. database.name: 数据库名称
 * 2. database.user: 数据库用户名
 * 3. database.password: 数据库密码
 * 4. 配置对象: 包含主机、方言、日志等配置
 */
const sequelize = new Sequelize(
  // 数据库名称
  database.name, 
  // 数据库用户名
  database.user, 
  // 数据库密码
  database.password, 
  {
    // 数据库主机地址
    host: database.host,
    // 数据库方言，这里使用PostgreSQL
    // dialect: 指定数据库类型，支持postgres、mysql、sqlite等
    dialect: database.dialect,
    // SQL日志配置：开发环境显示SQL日志，生产环境不显示
    // logging: 日志输出函数，开发时显示SQL语句便于调试
    logging: process.env.NODE_ENV === 'development' ? console.log : false,
    // 连接池配置
    // pool: 连接池配置，用于管理数据库连接，提高性能
    pool: {
      max: 10,      // 最大连接数，同时最多允许10个连接
      min: 0,       // 最小连接数，保持的最小连接数
      acquire: 30000, // 获取连接的超时时间（毫秒），30秒内无法获取连接则报错
      idle: 10000    // 连接空闲时间（毫秒），10秒后空闲连接会被释放
    }
  }
);

/**
 * 测试数据库连接
 * @async
 * @function testConnection
 * @returns {Promise<void>}
 * 
 * 该函数用于验证数据库连接是否正常
 * 使用sequelize.authenticate()方法测试连接
 * authenticate(): 异步方法，验证数据库连接是否成功
 */
async function testConnection() {
  try {
    // 尝试连接数据库
    // authenticate(): 验证数据库连接是否成功
    await sequelize.authenticate();
    // 数据库连接成功
    console.log('✅ 数据库连接成功');
  } catch (error) {
    // 连接失败，输出错误信息
    console.error('❌ 数据库连接失败:', error.message);
  }
}

/**
 * 按顺序同步数据库模型（仅在开发环境中使用）
 * 该函数会根据模型定义自动创建或更新数据库表结构
 * 首先尝试使用alter模式来保留现有数据，如果失败则根据环境变量决定是否使用force模式
 * @async
 * @function syncModels
 * @returns {Promise<void>}
 * 
 * 注意事项:
 * 1. 仅在开发环境中执行模型同步
 * 2. 使用alter模式保留现有数据
 * 3. 生产环境中应避免使用force模式
 * 
 * sync(): Sequelize同步方法
 * alter: true - 比较模型与数据库结构，只进行必要的更改，保留数据
 * force: true - 强制重新创建表，会丢失所有数据
 * 4. 如果需要使用force模式，需要设置ALLOW_FORCE_SYNC=true环境变量
 */
async function syncModels() {
  // 引入配置文件
  const config = require('../config');
  
  // 检查是否启用数据库同步
  if (!config.databaseSync.enabled) {
    console.log('ℹ️ 数据库同步已禁用');
    return;
  }
  
  // 仅在开发环境中执行模型同步
  // 防止在生产环境中意外修改数据库结构
  if (process.env.NODE_ENV === 'development') {
    try {
      // 使用alter模式同步模型，这会保留现有数据并只修改表结构
      // alter模式会比较当前模型与数据库表结构，只进行必要的更改
      // sync({ alter: true }): 同步模型到数据库，保留现有数据
      await sequelize.sync({ alter: true });
      // 数据库模型同步成功（数据已保留）
      console.log('✅ 数据库模型同步成功（数据已保留）');
      
      // 根据配置决定是否清理数据库中已删除模型对应的表
      if (config.databaseSync.autoCleanupDeletedTables) {
        await cleanupDeletedModelTables();
      } else {
        console.log('ℹ️ 数据库表清理已禁用，跳过清理操作');
      }
    } catch (error) {
      // 如果alter模式失败，输出错误信息
      console.error('❌ 数据库模型同步失败:', error.message);
      
      // 注意：force模式会删除并重新创建表，会导致数据丢失，仅用于特殊情况
      // 在生产环境中应避免使用force模式
      // 根据.env文件中的ALLOW_FORCE_SYNC配置来决定是否使用force模式
      if (process.env.NODE_ENV === 'development' && config.databaseSync.allowForceSync) {
        try {
          console.log('🔄 尝试使用force模式重新创建表...');
          // 按照表之间的约束关系顺序重建表结构
          await syncModelsInOrder(true);
          console.log('✅ 数据库模型强制同步成功');
        } catch (forceError) {
          console.error('❌ 数据库模型强制同步失败:', forceError.message);
        }
      } else {
        console.log('ℹ️ 未启用强制同步模式或不在开发环境，跳过强制同步');
      }
    }
  } else {
    console.log('ℹ️ 非开发环境，跳过数据库同步');
  }
}

/**
 * 清理数据库中已删除模型对应的表
 * @async
 * @function cleanupDeletedModelTables
 * @returns {Promise<void>}
 * 
 * 该函数会查询数据库中所有表，然后删除那些没有对应模型文件的表
 * 注意：这仅在开发环境中执行，以防止意外删除生产环境中的数据
 */
async function cleanupDeletedModelTables() {
  // 仅在开发环境中执行清理操作
  if (process.env.NODE_ENV !== 'development') {
    console.log('ℹ️ 非开发环境，跳过表清理操作');
    return;
  }
  
  try {
    // 获取当前模型列表
    const initModels = require('../models/init-models');
    const models = initModels(sequelize, Sequelize);
    // 获取模型键名作为表名
    const modelNames = Object.keys(models);
    
    // 查询数据库中所有的表，使用Sequelize提供的方法
    const queryInterface = sequelize.getQueryInterface();
    const tableNames = await queryInterface.showAllTables();
    
    // 定义应该保留的表名（包括模型表和其他系统表）
    const tablesToKeep = [
      ...modelNames,  // 模型对应的表
      'sequelize_meta' // Sequelize迁移元数据表
    ];
    
    console.log('🔍 检查数据库表清理...');
    console.log(`📊 当前数据库中的表: ${tableNames.join(', ')}`);
    console.log(`📋 当前模型列表: ${modelNames.join(', ')}`);
    
    // 遍历所有表，删除没有对应模型的表
    for (const tableName of tableNames) {
      // 跳过系统表和需要保留的表
      if (tablesToKeep.includes(tableName)) {
        console.log(`✅ 保留表: ${tableName}`);
        continue;
      }
      
      // 如果表名不在模型列表中，删除这些表
      console.log(`⚠️ 发现可能需要清理的表: ${tableName}`);
      
      // 在开发环境中，我们可以自动删除这些表
      // 注意：在生产环境中应该非常谨慎，不应该自动删除
      try {
        await queryInterface.dropTable(tableName);
        console.log(`✅ 已删除表: ${tableName}`);
      } catch (error) {
        console.warn(`⚠️ 删除表 ${tableName} 时出错:`, error.message);
      }
    }
    
    console.log('✅ 表清理操作完成');
  } catch (error) {
    console.warn('⚠️ 表清理操作失败:', error.message);
  }
}

/**
 * 按照表之间的约束关系顺序同步数据库模型
 * @param {boolean} force - 是否使用force模式
 * @async
 * @function syncModelsInOrder
 * @returns {Promise<void>}
 */
async function syncModelsInOrder(force = false) {
  // 动态导入模型定义
  const initModels = require('../models/init-models');
  const models = initModels(sequelize, Sequelize);
  
  // 定义表同步顺序（按照外键依赖关系排列）
  // 先同步没有外键依赖或依赖较少的表
  const syncOrder = [
    // 无外键依赖的基础表
    'users',
    'customers',
    'suppliers',
    'product_categories',
    'courier_companies',
    'express_info',
    
    // 依赖基础表的表
    'products',
    'purchase_orders',
    'sales_orders',
    
    // 依赖其他表的表
    'purchase_order_items',
    // 发货单表
    'delivery_orders'
  ];
  
  // 如果使用force模式，按顺序删除所有表后再重新创建
  if (force) {
    console.log('🔄 按顺序重建表结构...');
    
    // 按相反顺序删除表（避免外键约束冲突）
    for (let i = syncOrder.length - 1; i >= 0; i--) {
      const modelName = syncOrder[i];
      const model = models[modelName];
      try {
        await model.drop();
        console.log(`✅ 已删除表: ${modelName}`);
      } catch (error) {
        console.warn(`⚠️ 删除表 ${modelName} 时出错:`, error.message);
      }
    }
    
    // 按顺序创建表
    for (const modelName of syncOrder) {
      const model = models[modelName];
      try {
        await model.sync({ force: true });
        console.log(`✅ 已创建表: ${modelName}`);
      } catch (error) {
        console.error(`❌ 创建表 ${modelName} 时出错:`, error.message);
        throw error;
      }
    }
  } else {
    // 非force模式下，直接同步所有模型
    console.log('🔄 按顺序同步表结构...');
    for (const modelName of syncOrder) {
      const model = models[modelName];
      try {
        await model.sync({ force: false });
        console.log(`✅ 已同步表: ${modelName}`);
      } catch (error) {
        console.error(`❌ 同步表 ${modelName} 时出错:`, error.message);
        throw error;
      }
    }
  }
}

// 导出Sequelize实例和相关函数
// sequelize: Sequelize实例，用于数据库操作
// testConnection: 测试数据库连接的函数
// syncModels: 同步数据库模型的函数
module.exports = {
  sequelize,      // Sequelize实例
  testConnection, // 数据库连接测试函数
  syncModels      // 数据库模型同步函数
};